input_buffer=""
init_terminal() {
- # Hide cursor to avoid flickering
- tput civis
+ # Hide cursor to avoid flickering
+ tput civis
- # Scroll enough for the prompt in case we're at the
- # bottom of the screen
- for i in $(seq 1 $PROMPT_LINES); do
- echo
- done
+ # Scroll enough for the prompt in case we're at the
+ # bottom of the screen
+ for i in $(seq 1 $PROMPT_LINES); do
+ echo
+ done
- # Move cursor to the top of the room we just made
- tput cuu $((PROMPT_LINES + 1))
+ # Move cursor to the top of the room we just made
+ tput cuu $((PROMPT_LINES + 1))
- # Save cursor position
- tput sc
+ # Save cursor position
+ tput sc
- # Exclude prompt from scrollable area
- tput csr 0 "$(($(tput lines) - PROMPT_LINES - 1))"
+ # Exclude prompt from scrollable area
+ tput csr 0 "$(($(tput lines) - PROMPT_LINES - 1))"
- # Changing scrollable area moves cursor, so put it back
- tput rc
+ # Changing scrollable area moves cursor, so put it back
+ tput rc
}
restore_terminal() {
- clear_status
- clear_prompt
+ clear_status
+ clear_prompt
- tput csr 0 "$(tput lines)"
- tput rc
- tput cnorm
+ tput csr 0 "$(tput lines)"
+ tput rc
+ tput cnorm
}
eval_javascript_in_gnome_shell() {
- json=$(mktemp)
- busctl call --user --json=short \
- org.gnome.Shell \
- /org/gnome/Shell \
- org.gnome.Shell \
- Eval "s" "$1" > "$json"
- result=$(jq '.data[0]' < "$json")
- output=$(jq '.data[1] | select(. != null and . != "") | try fromjson catch .' < "$json")
- rm -f "$json"
-
- if [ "$result" = "false" ]; then
- echo -e "\e[31m${output:1:-1}\e[0m" > /dev/stderr
- return 1
- fi
-
- echo "$output"
- return 0
+ json=$(mktemp)
+ busctl call --user --json=short \
+ org.gnome.Shell \
+ /org/gnome/Shell \
+ org.gnome.Shell \
+ Eval "s" "$1" > "$json"
+ result=$(jq '.data[0]' < "$json")
+ output=$(jq '.data[1] | select(. != null and . != "") | try fromjson catch .' < "$json")
+ rm -f "$json"
+
+ if [ "$result" = "false" ]; then
+ echo -e "\e[31m${output:1:-1}\e[0m" > /dev/stderr
+ return 1
+ fi
+
+ echo "$output"
+ return 0
}
eval_javascript_in_looking_glass() {
- # encode the text so we can side-step complicated escaping rules
- ENCODED_TEXT=$(echo -n "$1" | hexdump -v -e '/1 "%02x"')
-
- eval_javascript_in_gnome_shell "
- const GLib = imports.gi.GLib;
- Main.createLookingGlass();
- const results = Main.lookingGlass._resultsArea;
- Main.lookingGlass._entry.text = '${ENCODED_TEXT}'.replace(/([0-9a-fA-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
- Main.lookingGlass._entry.clutter_text.activate();
- GLib.timeout_add(GLib.PRIORITY_DEFAULT, 125, () => {
- const index = results.get_n_children() - 1;
- if (index < 0)
- return;
- const resultsActor = results.get_children()[index];
- const output = \`\${resultsActor.get_children()[1].get_children()[0].text}\${resultsActor.get_children()[1].get_children()[1].get_children()[0].text}\`;
- Main.lookingGlass._lastEncodedResult = output.split('').map(char => char.charCodeAt(0).toString(16).padStart(2, '0')).join('');
- });
- " > /dev/null
-
- if [ $? -ne 0 ]; then
- return
- fi
-
- sleep .250
-
- OUTPUT=$(echo -e $(eval_javascript_in_gnome_shell 'Main.lookingGlass._lastEncodedResult;' | sed 's/\([[:xdigit:]][[:xdigit:]]\)/\\x\1/g'))
-
- if [ -z "$OUTPUT" ]; then
- echo -e "\e[31mCould not fetch result from call\e[0m" > /dev/stderr
- return
- fi
-
- eval_javascript_in_gnome_shell "delete Main.lookingGlass._lastEncodedResult;" > /dev/null
-
- echo ">>> $1"
- echo "${OUTPUT}"
+ # encode the text so we can side-step complicated escaping rules
+ ENCODED_TEXT=$(echo -n "$1" | hexdump -v -e '/1 "%02x"')
+
+ eval_javascript_in_gnome_shell "
+ const GLib = imports.gi.GLib;
+ Main.createLookingGlass();
+ const results = Main.lookingGlass._resultsArea;
+ Main.lookingGlass._entry.text = '${ENCODED_TEXT}'.replace(/([0-9a-fA-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
+ Main.lookingGlass._entry.clutter_text.activate();
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, 125, () => {
+ const index = results.get_n_children() - 1;
+ if (index < 0)
+ return;
+ const resultsActor = results.get_children()[index];
+ const output = \`\${resultsActor.get_children()[1].get_children()[0].text}\${resultsActor.get_children()[1].get_children()[1].get_children()[0].text}\`;
+ Main.lookingGlass._lastEncodedResult = output.split('').map(char => char.charCodeAt(0).toString(16).padStart(2, '0')).join('');
+ });
+ " > /dev/null
+
+ if [ $? -ne 0 ]; then
+ return
+ fi
+
+ sleep .250
+
+ OUTPUT=$(echo -e $(eval_javascript_in_gnome_shell 'Main.lookingGlass._lastEncodedResult;' | sed 's/\([[:xdigit:]][[:xdigit:]]\)/\\x\1/g'))
+
+ if [ -z "$OUTPUT" ]; then
+ echo -e "\e[31mCould not fetch result from call\e[0m" > /dev/stderr
+ return
+ fi
+
+ eval_javascript_in_gnome_shell "delete Main.lookingGlass._lastEncodedResult;" > /dev/null
+
+ echo ">>> $1"
+ echo "${OUTPUT}"
}
jump_to_prompt() {
- # Move to the bottom of the terminal
- tput cup $(($(tput lines) - PROMPT_LINES)) 0
+ # Move to the bottom of the terminal
+ tput cup $(($(tput lines) - PROMPT_LINES)) 0
}
clear_prompt() {
- jump_to_prompt
- tput el
+ jump_to_prompt
+ tput el
}
jump_to_status() {
- # Move to just below the prompt
- tput cup $(($(tput lines) - PROMPT_LINES + 1)) 0
+ # Move to just below the prompt
+ tput cup $(($(tput lines) - PROMPT_LINES + 1)) 0
}
clear_status() {
- jump_to_status
- tput el
+ jump_to_status
+ tput el
}
print_status_line() {
- jump_to_status
- echo -ne "Type quit to exit, ^G to inspect, ^L to clear screen"
- tput rc
+ jump_to_status
+ echo -ne "Type quit to exit, ^G to inspect, ^L to clear screen"
+ tput rc
}
clear_screen() {
- clear
- ask_user_for_input
+ clear
+ ask_user_for_input
}
ask_user_for_input() {
- # Save cursor position
- tput sc
+ # Save cursor position
+ tput sc
- clear_prompt
+ clear_prompt
- tput cnorm
- read -i "$READLINE_LINE" -p ">>> " -re input_buffer
- STATUS="$?"
- tput civis
+ tput cnorm
+ read -i "$READLINE_LINE" -p ">>> " -re input_buffer
+ STATUS="$?"
+ tput civis
- [ $STATUS != 0 ] && exit
+ [ $STATUS != 0 ] && exit
- if [ "$input_buffer" = "quit" -o "$input_buffer" = "q" -o "$input_buffer" = "exit" ]; then
- exit
- fi
+ if [ "$input_buffer" = "quit" -o "$input_buffer" = "q" -o "$input_buffer" = "exit" ]; then
+ exit
+ fi
- # Save input to history
- history -s "$input_buffer"
+ # Save input to history
+ history -s "$input_buffer"
- # Move cursor back to saved position before output
- tput rc
+ # Move cursor back to saved position before output
+ tput rc
}
quit_message() {
- print_status_line
- ask_user_for_input
+ print_status_line
+ ask_user_for_input
}
load_history() {
- while IFS= read -r line; do
- history -s "$line"
- done < <(eval_javascript_in_gnome_shell 'Main.lookingGlass._history._history.join("\n");' | jq -r '. | select(. != null and . != "") | tostring')
+ while IFS= read -r line; do
+ history -s "$line"
+ done < <(eval_javascript_in_gnome_shell 'Main.lookingGlass._history._history.join("\n");' | jq -r '. | select(. != null and . != "") | tostring')
}
check_for_unsafe_mode() {
- unsafe_mode=$(eval_javascript_in_gnome_shell 'global.context.unsafe_mode')
+ unsafe_mode=$(eval_javascript_in_gnome_shell 'global.context.unsafe_mode')
- if [ "$unsafe_mode" != "true" ]; then
- echo -e "Please enable unsafe-mode in the Flags tab of looking glass." > /dev/stderr
- exit
- fi
+ if [ "$unsafe_mode" != "true" ]; then
+ echo -e "Please enable unsafe-mode in the Flags tab of looking glass." > /dev/stderr
+ exit
+ fi
}
eval_autocomplete_javascript() {
- ENCODED_TEXT=$(echo -n "$1" | hexdump -v -e '/1 "%02x"')
- OUTPUT=$(eval_javascript_in_gnome_shell '
- const AsyncFunction = async function () {}.constructor;
-
- const command = `
- const JsParse = await import("resource:///org/gnome/shell/misc/jsParse.js");
-
- function getGlobalState() {
- const keywords = ["true", "false", "null", "new"];
- const windowProperties = Object.getOwnPropertyNames(globalThis).filter(
- a => a.charAt(0) !== "_");
- const headerProperties = JsParse.getDeclaredConstants(commandHeader);
-
- return keywords.concat(windowProperties).concat(headerProperties);
- }
-
- const commandHeader = '"'"'const {Clutter, Gio, GLib, GObject, Meta, Shell, St} = imports.gi; const Main = await import("resource:///org/gnome/shell/ui/main.js"); const inspect = Main.lookingGlass.inspect.bind(Main.lookingGlass); const it = Main.lookingGlass.getIt(); const r = Main.lookingGlass.getResult.bind(Main.lookingGlass);'"'"';
-
- const text="'${ENCODED_TEXT}'".replace(/([0-9a-fA-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
- const completions = await JsParse.getCompletions(text, commandHeader, getGlobalState());
- return {
- "completions": completions[0],
- "attrHead": completions[1]
- };`;
- AsyncFunction(command)();
- ')
- if [ $? = 1 ]; then
- echo fail
- fi
- echo "$OUTPUT"
+ ENCODED_TEXT=$(echo -n "$1" | hexdump -v -e '/1 "%02x"')
+ OUTPUT=$(eval_javascript_in_gnome_shell '
+ const AsyncFunction = async function () {}.constructor;
+
+ const command = `
+ const JsParse = await import("resource:///org/gnome/shell/misc/jsParse.js");
+
+ function getGlobalState() {
+ const keywords = ["true", "false", "null", "new"];
+ const windowProperties = Object.getOwnPropertyNames(globalThis).filter(
+ a => a.charAt(0) !== "_");
+ const headerProperties = JsParse.getDeclaredConstants(commandHeader);
+
+ return keywords.concat(windowProperties).concat(headerProperties);
+ }
+
+ const commandHeader = '"'"'const {Clutter, Gio, GLib, GObject, Meta, Shell, St} = imports.gi; const Main = await import("resource:///org/gnome/shell/ui/main.js"); const inspect = Main.lookingGlass.inspect.bind(Main.lookingGlass); const it = Main.lookingGlass.getIt(); const r = Main.lookingGlass.getResult.bind(Main.lookingGlass);'"'"';
+
+ const text="'${ENCODED_TEXT}'".replace(/([0-9a-fA-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
+ const completions = await JsParse.getCompletions(text, commandHeader, getGlobalState());
+ return {
+ "completions": completions[0],
+ "attrHead": completions[1]
+ };`;
+AsyncFunction(command)();
+')
+if [ $? = 1 ]; then
+ echo fail
+fi
+echo "$OUTPUT"
}
autocomplete() {
- RESULT="$(eval_autocomplete_javascript "$READLINE_LINE")"
- COMPLETIONS="$(echo "$RESULT" | jq '.completions[]' | sed 's/\"//g')"
- ATTR_HEAD=$(echo "$RESULT" | jq '.attrHead' | sed 's/\"//g')
- N_COMPLETIONS=$(echo "$COMPLETIONS" | wc -w)
- if [ $N_COMPLETIONS = 0 ]; then
- return
- elif [ $N_COMPLETIONS = 1 ]; then
- TO_ADD=$(echo $COMPLETIONS | sed s/$ATTR_HEAD//)
- READLINE_LINE+="$TO_ADD"
- (( READLINE_POINT += $(echo "$TO_ADD" | wc -c) ))
- else
- tput rc
- echo
- echo "$COMPLETIONS" | pr -T -2 -o 4
- ask_user_for_input
- fi
+ RESULT="$(eval_autocomplete_javascript "$READLINE_LINE")"
+ COMPLETIONS="$(echo "$RESULT" | jq '.completions[]' | sed 's/\"//g')"
+ ATTR_HEAD=$(echo "$RESULT" | jq '.attrHead' | sed 's/\"//g')
+ N_COMPLETIONS=$(echo "$COMPLETIONS" | wc -w)
+ if [ $N_COMPLETIONS = 0 ]; then
+ return
+ elif [ $N_COMPLETIONS = 1 ]; then
+ TO_ADD=$(echo $COMPLETIONS | sed s/$ATTR_HEAD//)
+ READLINE_LINE+="$TO_ADD"
+ (( READLINE_POINT += $(echo "$TO_ADD" | wc -c) ))
+ else
+ tput rc
+ echo
+ echo "$COMPLETIONS" | pr -T -2 -o 4
+ ask_user_for_input
+ fi
}
inspect() {
- eval_javascript_in_gnome_shell '
- const AsyncFunction = async function () {}.constructor;
-
- delete Main.lookingGlass._lastInspection;
- const command = `
- const Main = await import("resource:///org/gnome/shell/ui/main.js");
- const LookingGlass = await import("resource:///org/gnome/shell/ui/lookingGlass.js");
-
- Main.lookingGlass._inspector = new LookingGlass.Inspector(Main.lookingGlass);
- const inspector = Main.lookingGlass._inspector;
-
- inspector.connectObject("target", (i, obj, stageX, stageY) => {
- let command = "inspect(" + Math.round(stageX) + ", " + Math.round(stageY) + ")";
- Main.lookingGlass._lastInspection = command;
- },
- "closed", () => {
- delete Main.lookingGlass._inspector;
- });
- `;
- AsyncFunction(command)();
- '
-
- while sleep .250; do
- finished=$(eval_javascript_in_gnome_shell 'Main.lookingGlass._inspector? false : true')
- last_inspection=$(eval_javascript_in_gnome_shell 'Main.lookingGlass._lastInspection' | jq -r)
-
- [ "$finished" = "true" ] && break
- [ -n "$last_inspection" ] && break
- done
-
- tput rc
- [ -n "$last_inspection" ] && eval_javascript_in_looking_glass "$last_inspection"
- eval_javascript_in_gnome_shell 'delete Main.lookingGlass._lastInspection; Main.lookingGlass.setBorderPaintTarget(null);' > /dev/null
-
- ask_user_for_input
+ eval_javascript_in_gnome_shell '
+ const AsyncFunction = async function () {}.constructor;
+
+ delete Main.lookingGlass._lastInspection;
+ const command = `
+ const Main = await import("resource:///org/gnome/shell/ui/main.js");
+ const LookingGlass = await import("resource:///org/gnome/shell/ui/lookingGlass.js");
+
+ Main.lookingGlass._inspector = new LookingGlass.Inspector(Main.lookingGlass);
+ const inspector = Main.lookingGlass._inspector;
+
+ inspector.connectObject("target", (i, obj, stageX, stageY) => {
+ let command = "inspect(" + Math.round(stageX) + ", " + Math.round(stageY) + ")";
+ Main.lookingGlass._lastInspection = command;
+ },
+"closed", () => {
+ delete Main.lookingGlass._inspector;
+});
+`;
+AsyncFunction(command)();
+'
+
+while sleep .250; do
+ finished=$(eval_javascript_in_gnome_shell 'Main.lookingGlass._inspector? false : true')
+ last_inspection=$(eval_javascript_in_gnome_shell 'Main.lookingGlass._lastInspection' | jq -r)
+
+ [ "$finished" = "true" ] && break
+ [ -n "$last_inspection" ] && break
+done
+
+tput rc
+[ -n "$last_inspection" ] && eval_javascript_in_looking_glass "$last_inspection"
+eval_javascript_in_gnome_shell 'delete Main.lookingGlass._lastInspection; Main.lookingGlass.setBorderPaintTarget(null);' > /dev/null
+
+ask_user_for_input
}
main_loop() {
- bind -x '"\a"':inspect 2> /dev/null
- bind -x '"\t"':autocomplete 2> /dev/null
- bind -x '"\f"':clear_screen 2> /dev/null
- while true; do
- ask_user_for_input
- eval_javascript_in_looking_glass "$input_buffer"
- done
+ bind -x '"\a"':inspect 2> /dev/null
+ bind -x '"\t"':autocomplete 2> /dev/null
+ bind -x '"\f"':clear_screen 2> /dev/null
+ while true; do
+ ask_user_for_input
+ eval_javascript_in_looking_glass "$input_buffer"
+ done
}
check_for_unsafe_mode